#include "Terrain.h"

namespace GeorgeLib
{

	Terrain::Terrain(void)
		: size(0)
	{
	}

	Terrain::~Terrain(void)
	{
		drop();
	}

	int Terrain::getSize(void)
	{
		return size;
	}

	void Terrain::drop(void)
	{
		DELETE_ARR(heightmap);
	}

	bool Terrain::LoadRAW(char * filename)
	{
		// Open the file in binary mode
		// And set the read position at the end of the file to get the file size
		ifstream file(filename, ios::in | ios::binary | ios::ate);

		try
		{
			if (!file.is_open())
				throw "Error opening file";

			// Get the file size
			if (file.tellg())
				size = (int)sqrt((float)file.tellg());
			else
				throw "File is empty";

			// Assign memory needed
			heightmap = new GLubyte[size * size];

			if (heightmap == NULL)
			{
				throw "Memory Allocation failed";
			}

			// Move read position to beginning of the file
			file.seekg(0, file.beg);

			file.read((char*)heightmap, size * size);

			cout << file.gcount() << endl;

			if (file.gcount() != size * size)
			{
				DELETE_ARR(heightmap);
				throw "Error reading heightmap";
			}

			file.close();
		}
		catch (char * error)
		{
			if (file.is_open()) file.close();
			Logger->AddLog("Terrain\t\t: LoadRAW(", true, false);
			*Logger << filename << ") - " << error << '\n';
			return false;
		}
		catch (...)
		{
			return false;
		}

		return true;
	}

	void Terrain::BindY(vec3 & p, float y_offset)
	{
		if (p.x > size || p.x < 0 ||
			p.y > MAP_MAX_HEIGHT * 1.5 || p.y < 0 ||
			p.z > size || p.z < 0) return;

		p.y = heightcollide[(int)p.z][(int)p.x] + y_offset;
	}

	void Terrain::draw(Texture texture)
	{
		if (heightmap == NULL)
		{
			Logger->AddLog("Terrain\t\t: draw(Texture) - Height map not loaded");
			return;
		}

		const float MAX_HEIGHT = MAP_MAX_HEIGHT; // constrain mountain height in 3D scene units
		const float SCALE_FACTOR = 256.0f / MAX_HEIGHT; // byte grey combination representation
		// height range(color diff per unit height)

		float scaledHeight, nextScaledHeight, color, nextColor;

		// Draw terrain
		glEnable(GL_TEXTURE_2D); // enable/disable to show shades for color and next color
		glBindTexture(GL_TEXTURE_2D, texture.id);

		// initialise the height map collision data
		heightcollide = new float *[size];
		for (int i = 0; i < size; ++i) heightcollide[i] = new float[size];

		glPushMatrix();

		glTranslatef((float)size, 0, (float)size);
		glRotatef(180, 0, 1, 0);
		glTranslatef((float)-size, 0, (float)-size);

		for (int z = 0; z < size - 1; ++z) // far -near
		{
			glBegin(GL_TRIANGLE_STRIP);

			for (int x = 0; x < size; ++x) // left right
			{
				// render 2 vertices of strip at a time
				scaledHeight = heightmap[z * size + x] / SCALE_FACTOR;
				nextScaledHeight = heightmap[(z + 1) * size + x] / SCALE_FACTOR;
				color = 0.5f + 0.5f * scaledHeight / MAX_HEIGHT; // brighter color shade
				nextColor = 0.5f + 0.5f * nextScaledHeight / MAX_HEIGHT;

				glColor3f(color, color, color); // shades of height
				glTexCoord2f((GLfloat)x / size, (GLfloat)z / size); //tile it with texture coords
				glVertex3f(static_cast<GLfloat>(x - size / 2) * 2, scaledHeight, static_cast<GLfloat>(z - size / 2) * 2);

				glColor3f(nextColor, nextColor, nextColor);
				glTexCoord2f((GLfloat)x / size, (GLfloat)(z + 1) / size);
				glVertex3f(static_cast<GLfloat>(x - size / 2) * 2, nextScaledHeight, static_cast<GLfloat>(z + 1 - size / 2) * 2);

				// set collision data
				heightcollide[z][x] = scaledHeight;
			}

			glEnd();
		}

		glPopMatrix();

		glDisable(GL_TEXTURE_2D);
	}

	void Terrain::draw(MultiTexture texture)
	{
		if (heightmap == NULL)
		{
			Logger->AddLog("Terrain\t\t: draw(Texture) - Height map not loaded");
			return;
		}

		const float MAX_HEIGHT = 30.0f; // constrain mountain height in 3D scene units
		const float SCALE_FACTOR = 256.0f / MAX_HEIGHT; // byte grey combination representation
		// height range(color diff per unit height)

		float scaledHeight, nextScaledHeight, color, nextColor;

		// Set Textures
		for (uint i = 0; i < texture.texArray.size(); ++i)
		{
			glActiveTextureARB(GL_TEXTURE0_ARB + i);
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, texture.texArray[i].id);
		}

		heightcollide = new float *[size];
		for (int i = 0; i < size; ++i) heightcollide[i] = new float[size];

		glPushMatrix();

		//glTranslatef(size,0,size);

		for (int z = 0; z < size - 1; ++z) // far -near
		{
			glBegin(GL_TRIANGLE_STRIP);

			for (int x = 0; x < size; ++x) // left right
			{
				// render 2 vertices of strip at a time
				scaledHeight = heightmap[z * size + x] / SCALE_FACTOR;
				nextScaledHeight = heightmap[(z + 1) * size + x] / SCALE_FACTOR;
				color = 0.5f + 0.5f * scaledHeight / MAX_HEIGHT; // brighter color shade
				nextColor = 0.5f + 0.5f * nextScaledHeight / MAX_HEIGHT;

				glColor3f(color, color, color); // shades of height
				for (uint i = 0; i < texture.texArray.size(); ++i)
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB + i, (GLfloat)x / size, (GLfloat)z / size);
				glVertex3f(static_cast<GLfloat>(size - x), scaledHeight, static_cast<GLfloat>(size - z));

				glColor3f(nextColor, nextColor, nextColor);
				for (uint i = 0; i < texture.texArray.size(); ++i)
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB + i, (GLfloat)x / size, (GLfloat)(z + 1) / size);
				glVertex3f(static_cast<GLfloat>(size - x), nextScaledHeight, static_cast<GLfloat>(size - (z + 1)));

				heightcollide[z][x] = scaledHeight;
			}

			glEnd();
		}

		for (uint i = 0; i < texture.texArray.size(); ++i)
		{
			glActiveTextureARB(GL_TEXTURE0_ARB + i);
			glDisable(GL_TEXTURE_2D);
		}

		/*  // Debug for collision map
			glPointSize(2); glColor3f(1,1,1);
			glRotatef(180,0,1,0);
			glTranslatef(-size,0,-size);

			for(int z = 0; z < size; ++z)
			for(int x = 0; x < size; ++x)
			{
			glBegin(GL_POINTS);
			glVertex3f(x, heightcollide[z][x], z);
			glEnd();
			}
			*/
		glPopMatrix();
	}

} // namespace GeorgeLib